package com.domain.common.id;

import java.net.InetAddress;

/**
 * ClassName: IdWorker<br>
 * Description: ID策略生成器<br>
 * 基于snowflake算法改造.<br>
 *
 * @version V1.0
 * @date: 2016年11月7日 下午4:14:31
 * @since JDK 1.8
 */
public class IdWorker {
    // 根据具体机器环境提供
    private final long workerId;
    // 滤波器,使时间变小,生成的总位数变小,一旦确定不能变动
    private final static long twepoch = 1361753741828L;
    private long sequence = 0L;
    private final static long workerIdBits = 10L;
    private final static long maxWorkerId = -1L ^ -1L << workerIdBits;
    private final static long sequenceBits = 12L;

    private final static long workerIdShift = sequenceBits;
    private final static long timestampLeftShift = sequenceBits + workerIdBits;
    private final static long sequenceMask = -1L ^ -1L << sequenceBits;

    private long lastTimestamp = -1L;
    // 根据主机id获取机器码
    private static IdWorker worker = new IdWorker();

    public IdWorker() {
        this.workerId = getAddress() % (IdWorker.maxWorkerId + 1);
    }

    public static Long getId() {
        return worker.nextId();
    }

    public synchronized long nextId() {
        long timestamp = this.timeGen();
        if (this.lastTimestamp == timestamp) {
            this.sequence = (this.sequence + 1) & IdWorker.sequenceMask;
            if (this.sequence == 0) {
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0;
        }
        if (timestamp < this.lastTimestamp) {
            try {
                throw new Exception(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds",
                        this.lastTimestamp - timestamp));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        this.lastTimestamp = timestamp;
        long nextId = ((timestamp - twepoch << timestampLeftShift)) | (this.workerId << IdWorker.workerIdShift)
                | (this.sequence);
        return nextId;
    }

    private long tilNextMillis(final long lastTimestamp1) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp1) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    private static long getAddress() {
        try {
            String currentIpAddress = InetAddress.getLocalHost().getHostAddress();
            String[] str = currentIpAddress.split("\\.");
            StringBuilder hardware = new StringBuilder();
            for (int i = 0; i < str.length; i++) {
                hardware.append(str[i]);
            }
            return Long.parseLong(hardware.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

        return 2L;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }
    // public static void main(final String[] args) {
    // for (int i = 0; i < 100; i++) {
    // System.out.println(getId());
    // }
    // }
}
